/**
 * 
 */
package org.youisoft.summer.evaluator.function;

import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.youisoft.summer.evaluator.InvokeException;
import org.youisoft.summer.evaluator.UnitEnum;
import org.youisoft.summer.evaluator.context.CalContext;
import org.youisoft.summer.evaluator.operator.Operator;
import org.youisoft.summer.evaluator.registry.DefaultFunctionRegistry;

/**
 * @author zijiang.jl
 *
 */
public abstract class AbstractFunction extends DefaultFunctionRegistry implements Function {
	protected static Log logger=LogFactory.getLog(Function.class);
	
	protected AtomicBoolean initilized=new AtomicBoolean(false);
	protected AtomicBoolean started=new AtomicBoolean(false);
	private UnitEnum type=UnitEnum.parenthesis;
	
	
	@Override
	public UnitEnum getType() {
		return this.type;
	}


	@Override
	public void setType(UnitEnum type) {
		this.type=type;
		
	}


	@Override
	public String[] getAlias() {
		return null;
	}


	@Override
	public String getName() {
		String name=this.getClass().getSimpleName();
		if(name.endsWith(Function.class.getSimpleName())){
			return name.substring(0,name.length()-Function.class.getSimpleName().length()).toLowerCase();
		}
		return null;
	}


	@Override
	public boolean isValidate() {
		return true;
	}
	

	@Override
	public boolean addendumOperator() {
		return UnitEnum.bracket.equals(this.getType());
	}


	@Override
	public final <C,R,P> boolean supportOperator(CalContext<C,R,P> context,Operator<?>... operator) {
		if(operator==null||operator.length<this.minOperatorCount()) return false;
		if(!this.checkContext(context)) return false;
		if(this.addendumOperator()){
			if((operator.length>0)
					&&!this.checkAddendumOperator(operator[operator.length-1])) return false;
			return this.checkOperator(operator.length-1, operator);
		}else{
			return this.checkOperator(operator.length, operator);
		}
	}
	

	
	
	protected <C,R,P> boolean checkContext(CalContext<C,R,P> context){
		return true;
	}

	
	private boolean checkOperator(int len,Operator<?>... operator){
		for(int i=0;i<len;i++){
			if(operator[i]==null) return false;
			switch(this.getType()){
				case brace:
					if(!this.checkBraceOperator(i, operator[i])) return false;
					break;
				case bracket:
					if(!this.checkBracketOperator(i, operator[i])) return false;
					break;
				default:
					if(!this.checkParenthesisOperator(i, operator[i])) return false;
			}
			
			
		}

		return true;
	}
	
	protected boolean checkBraceOperator(int index,Operator<?> operator){
		return this.checkOperator(index, operator);
	}
	
	protected boolean checkBracketOperator(int index,Operator<?> operator){
		return this.checkOperator(index, operator);
	}
	
	protected boolean checkParenthesisOperator(int index,Operator<?> operator){
		return this.checkOperator(index, operator);
	}
	
	
	protected boolean checkOperator(int index,Operator<?> operator){
		return true;
	}
	
	protected boolean checkAddendumOperator(Operator<?> operator){
		return true;
	}


	@Override
	public <C,R,P> boolean isIgnore(CalContext<C,R,P> context) {
		return false;
	}
	
	public <C, R, P> void beforeInvoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException{
		for(int i=0;i<operator.length;i++){
			operator[i]=operator[i].prepareCall(context);
		}
		if(context!=null){
			if(context.isNeedReInit()) this.reInit();
		}
	}
	
	@Override
	public <C, R, P> Operator<?> invoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException{
		this.beforeInvoke(context, operator);
		Operator<?> op=null;
		switch(this.getType()){
			case brace:
				op=this.doBraceInvoke(context, operator);
				break;
			case bracket:
				op=this.doBracketInvoke(context, operator);
				break;
			default:
				op=this.doParenthesisInvoke(context, operator);
		}
		
		if(op==null) op=Operator.nullInstance;
		this.afterInvoke(context, operator);
		return op;
	}
	
	public <C, R, P> void afterInvoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException{
		for(int i=0;i<operator.length;i++){
			operator[i]=operator[i].postCall(context);
		}
	}
	

	
	public <C, R, P> Operator<?> doInvoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException{
		return null;
	}
	
	
	public <C, R, P> Operator<?> doBraceInvoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException{
		return this.doInvoke(context, operator);
	}
	
	
	public <C, R, P> Operator<?> doBracketInvoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException{
		return this.doInvoke(context, operator);
	}
	
	public <C, R, P> Operator<?> doParenthesisInvoke(CalContext<C, R, P> context,
			Operator<?>... operator) throws InvokeException{
		return this.doInvoke(context, operator);
	}

	@Override
	public <C,R,P> Operator<?> invoke(CalContext<C,R,P> context,FunctionCallback callback,Operator<?> ... operator) throws InvokeException{
		Operator<?> op=null;
		switch(this.getType()){
			case brace:
				op=callback.doBraceInvoke(context, operator);
				break;
			case bracket:
				op=callback.doBracketInvoke(context, operator);
				break;
			default:
				op=callback.doParenthesisInvoke(context, operator);
		}
		return op;
	}
	
	
	protected int minOperatorCount(){
		return 0;
	}



	@Override
	public void destory() {
		
		
	}


	@Override
	public void init() {
		initilized.set(true);
	}
	
	


	@Override
	public boolean isInit() {
		
		return initilized.get();
	}


	@Override
	public void reInit() {
		this.init();
	}


	@Override
	public void clear() {

		
	}
	
	

}
